#ifndef __MY__SERVER__
#define __MY__SERVER__
#include <ctime>
#include <sstream>
#include "httplib.h"

extern Cloude::DataManager* _dataM;

namespace Cloude
{
	/*
		http服务器只提供3种请求的响应:
				1./download  --- 下载请求
				2./upload    --- 上传请求
				3./list      --- 获取列表请求 
		
	*/
	
	using std::string;
	using std::endl;
	using std::cout;
	using std::vector;
	// 上层应用提供的服务
	class Service
	{
	private:
	string _server_ip;
	int _server_port;
	string _url_prefix;
	httplib::Server _srv;	

	public:
		Service()
		{
			Cloude::Config* config = Cloude::Config::GetInstance();
			_server_ip = config->GetServerIp();
			_server_port = std::stoi(config->GetServerPort());
			_url_prefix = config->GetUrlPre();
		}
		
		// ETag: 唯一标识该资源 -- 请求/响应中那份资源 -- 文件
		// ETag用于判断客户端资源是否更改过
		// 我们这里ETag 设置为 文件名+大小+修改时间
		string GetETag(string& filename)
		{
			FileUtil fu(filename);
			string str = fu.FileName();
			str += std::to_string(fu.GetFileSize()); 
			str += std::to_string(fu.GetMTime()); 
			return str;
		}

		// 1./download  --- 下载请求
		// 下载请求: GET /download/a.txt http/1.1
		void DownLoad(const httplib::Request& req, httplib::Response& rsp)
		{
			// 1.解析请求
			// 1.1 根据url,获取BackupInfo
			string url = req.path;
			BackupInfo info;
			cout << "收到一个请求" << url << endl;
			if(_dataM->GetOneByUrl(url,&info) == false)
			{
				cout << "无" << url << endl;
				rsp.status = 404;
				return;	
			}
			// 1.2 检测该文件是否压缩
			if(info._pack_flag == true)
			{
				// 1.3 进行解压缩
				FileUtil fp(info._pack_path);
				fp.UnCompress(info._back_path);

				// 1.4 删除压缩文件并修改压缩标志 这个一定不能忘记！
				fp.RemoveFile();
				info._pack_flag = false;
				_dataM->Update(info);
			}
			// 2.构建响应
		
			// 2.1 判断是否是断点续传
			// 当下载请求中存在 If-Range时，表明这是一个断点续传
			// If-Range 和 ETag值是一样的
			// Range: 告诉服务器下载的区间范围
			bool resume = false;  // 断点续传标志
			if(req.has_header("If-Range") == true)
			{
				// 前侧前后If-Range ETag是否一致
				string if_range = req.get_header_value("If-Range");
				string ETag = GetETag(info._back_path);
				if(if_range == ETag)
				{
					resume = true;
					cout << "是一个断点续传的请求 " << endl;
				}
				else
				{
					cout << "是一个断点续传的请求 " << endl;
				}
			}
			// 2.2 填充报头
			FileUtil fb(info._back_path);
			if(resume == true) // 断点续传
			{
				// 继续解析请求
				// Range: bytes=89-999， 告诉服务器，要获取文件[89,999]区间的数据  
				// FileUtil内部提供GetPosLen 获取文件指定长度
				
				// string range = req.get_header_value("Range"); // bytes=begin-end
				// GetPosLen(str,begin,end-begin+1)

				//httplib内部实现了对于区间请求也就是断点续传请求的处理
				//只需要我们用户将文件所有数据读取到rsp.body中，它内部会自动根据请求
				//区间，从body中取出指定区间数据进行响应
				// 因为httplib内部已经支持断点续传了,我们在这里不能写就意思一下
				
				fb.GetContent(&rsp.body);
			
				// HTTP/1.1 206 Partial Content
				rsp.version = "HTTP/1.1";
				rsp.status = 206; // 注意这个!!!状态码
				rsp.set_header("Accept-Ranges","bytes");
				rsp.set_header("Content-Type","application/octet-stream");
				rsp.set_header("ETag",GetETag(info._back_path));
				// Content-Range: bytes 89-999/10000  [ ,]/filesize
				//rsp.set_header("Content-Range","bytes begin-end/size");
				cout << "断点续传，响应构建完成" << endl;
			}
			else // 正常下载
			{
				fb.GetContent(&rsp.body);
				rsp.version = "HTTP/1.1";
				rsp.status = 200;
				rsp.set_header("Accept-Ranges","bytes");
				rsp.set_header("ETag",GetETag(info._back_path));
				rsp.set_header("Content-Type","application/octet-stream");
				cout << "正常下载，响应构建完成" << endl;
			}
		
		}
		// 2./upload    --- 上传请求
		// POST /upload HTTP/1.1
		// 客户端上传了一个表单,我们需要解析表单看是否有文件请求
		void UpLoad(const httplib::Request& req, httplib::Response& rsp)
		{
			// 1.解析请求
			if(req.has_file("file") == false) // 判断有没有文件区域
			{
				rsp.status = 404;
				return;
			}
			// 1.1 获取文件区域的表单
			auto file = req.get_file_value("file");
			// a.txt
			// 1.2 获取文件名，内容
			//string filename = file.filename;
			string filename = Cloude::FileUtil(file.filename).FileName();
			string content = file.content;
			cout << "收到一个请求: " << "/uplaod/" << filename << endl;
		
			// 保存：这里写的比较简陋，判断文件是否存在，不存在保存
			// 如果存在，判断md5是否相同,这里直接一锅端了，直接覆盖式的备份文件
			// 1.3 备份	
			Cloude::Config* config = Cloude::Config::GetInstance();
			// ./back_dir/a.txt
			string back_path = config->GetBackDir() + filename;
			Cloude::FileUtil fu(back_path);
			fu.SetContent(content);
		
			// 1.4 填充BackupInfo,并更新DataManager
			// NewBackupInfo内部要求先创建文件
			Cloude::BackupInfo info;
			info.NewBackupInfo(back_path);
			_dataM->Insert(info);
			cout << "备份成功" << endl;
			// 2. 构建响应
			rsp.status = 200;
			rsp.version = "HTTP/1.1";
		}
		
		string TimeToStr(time_t& time)
		{
			return ctime(&time);
		}
		
		// 3./list/back_dir   --- 获取列表请求 --> 给他back_dir目录下的备份文件信息
		// 根路径/ 也对应的是这个响应
		// Get /listshow  --> 获取列表请求 
		// 响应： 给客户端响应 备份的所有文件信息 url  atime mtime sz
		// 未来客户端可以点击url进行下载,url --> /download/xxx.txt
		//
		// 场景如下：这是一个回调机制，由上层用户自己响应。当客户端来了一个list请求
		// 其内部自动回调 我们写的List函数，进行响应。
		
		void ListShow(const httplib::Request& req, httplib::Response& rsp)
		{
			
			// 用string 去格式控制html 太傻了,使用stringstream!
			//string str = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />";
			//str += "<title>Page of Download</title></head>";
			//for(int i = 0; i < arry.size(); i++)
			//{
			//	
			//	str += "<body> <h1>Download</h1><table>";
			//	str += "<tr><td><a href=";
			//	str += arry[i]._url;
			//	str +=">";
			//	str += arry[i]._filename;
			//	str += "</a></td>";
			//	str += "<td align='right'>";
			//	str += TimeToStr(arry[i]._atime);
			//	str += "</td>";
			//	str += "<td align='right'>";
			//	str += std::to_string(arry[i]._filesz / 1024);
			//	str += "K </td>";
			//}
			//str += "</tr></table></body></html>";
			//rsp.body = str;	
			// 状态行  协议版本 状态码 状态码描述  HTTP/1.1 200 OK
			


			// 1.获取备份文件的信息
			vector<Cloude::BackupInfo> arry;
			_dataM->GetAll(&arry);
			// 2.构建响应	
			// 有效载荷 --> html
			// 用string 去格式控制html 太傻了,使用stringstream!
			std::stringstream ss;
			ss << "<html><head>";
			ss << "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />";
			ss << "<title>Page of Download</title>";
			ss << "</head><body><h1>Download</h1><table>";
			for(auto& info : arry)
			{
				cout << info._filename << endl;
				ss << "<tr>";

				ss << "<td><a href='" << info._url << "'>" << info._filename << " </a></td>";
				ss << "<td align='right'>" << TimeToStr(info._atime) << "</td>";
				ss << "<td align='right'>" << std::to_string(info._filesz / 1024) << "K </td>"; 

				ss << "</tr>";

			}
		    ss << "</table></body></html>";
			rsp.body = ss.str();
			rsp.version = "HTTP/1.1";
			rsp.status = 200;
			//rsp.reason = "OK";
			// 响应报头
			rsp.set_header("Content-Type","text/html");
			
		}

		void RunMoudle()
		{
			_srv.Get("/list",std::bind(&Service::ListShow,this,std::placeholders::_1,std::placeholders::_2 ));
			_srv.Get("/",std::bind(&Service::ListShow,this,std::placeholders::_1,std::placeholders::_2 ));
			// 下面那么写是为了解决
			//_srv.Post("/upload",std::bind(&Service::UpLoad, this, std::placeholders::_1, std::placeholders::_2)); 
			// 引起的函数重载问题
			using HandlerType = std::function<void(const httplib::Request&, httplib::Response&)>;  
			HandlerType handler = std::bind(&Service::UpLoad, this, std::placeholders::_1, std::placeholders::_2); 

			_srv.Post("/upload",handler); 	
			// (.*) 这是一个正则表达式
			_srv.Get("/download/(.*)",std::bind(&Service::DownLoad,this,std::placeholders::_1,std::placeholders::_2 ));
			_srv.listen(_server_ip.c_str(),_server_port);
		}

	};
}


#endif
